package pt.inescid.components.policyengine.heimdall.pdp.base;

import java.util.Vector;

import pt.inescid.components.policyengine.heimdall.pap.base.CesAggDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.CompositeDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.EventFieldDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.ExternalMethodDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.RuleDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.SimpleDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.SubPolicyDefinition;
import pt.inescid.components.policyengine.heimdall.pdp.PolicyDecision;

public class CompositeInstance extends RuleInstance {

	private Vector<RuleInstance> operands;
	private CompositeDefinition definition;
	
//  ----------------------------------------------------------
//  ------------------------   constructor   ------------------------
//  ----------------------------------------------------------
	public CompositeInstance(CompositeDefinition definition, 
			ModelInstance currentModel,
			PolicyInstance currentPolicy) {

		super(definition);
		
		operands = new Vector<RuleInstance>();
		Vector allOperandDefs = definition.getOperands();
		RuleDefinition currentOperandDef = null;
		for (int i = 0; i < allOperandDefs.size(); i++) {
			currentOperandDef = (RuleDefinition) allOperandDefs.elementAt(i);		
					
			operands.add(RuleInstance.getInstance(currentOperandDef, currentModel, currentPolicy));			
		}
		
		this.definition = definition;
	}

	
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      evaluate       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public Object evaluate(ModelInstance currentModel, boolean onlyCached) {
		
		Object finalResult = null;
		RuleInstance currentOperand = null;
		PolicyDecision currentDecision = currentModel.getDecision();

		
//	  ----------------------------------------------------------
//	  ------------------------   BOOLEAN   ------------------------
//	  ----------------------------------------------------------
		if (definition.isBooleanExpression()) {
			// init
			boolean result = false;
			switch (definition.getType()) {
			case RuleDefinition.LOGICAL_AND_OPERATOR:
				result = true;
				break;					
			case RuleDefinition.LOGICAL_OR_OPERATOR:
				result = false;
				break;					
			default:
				break;
			}

			for (int i = 0; i < operands.size(); i++) {
				currentOperand = (RuleInstance) operands.elementAt(i);
				
				switch (definition.getType()) {

//				  ----------------------------------------------------------
//				  ------------------------   AND   ------------------------
//				  ----------------------------------------------------------
				case RuleDefinition.LOGICAL_AND_OPERATOR:
					result = result && ((Boolean) currentOperand.evaluate(
							currentModel, onlyCached)).
							booleanValue();
					
					if (result == false) {
						i = operands.size();
					}
					else if (onlyCached) {
						onlyCached = false;

						result = result && ((Boolean) currentOperand.evaluate(
								currentModel, onlyCached)).
								booleanValue();						
					}
					
					break;					

//					  ----------------------------------------------------------
//					  ------------------------   OR   ------------------------
//					  ----------------------------------------------------------
				case RuleDefinition.LOGICAL_OR_OPERATOR:
					result = result || ((Boolean) currentOperand.evaluate(
							currentModel, onlyCached)).
							booleanValue();

					if (result == true) {
						i = operands.size();
					}
					else if (onlyCached) {
						onlyCached = false;

						result = result && ((Boolean) currentOperand.evaluate(
								currentModel, onlyCached)).
								booleanValue();						
					}
					
					break;					

//					  ----------------------------------------------------------
//					  ------------------------   NOT   ------------------------
//					  ----------------------------------------------------------
				case RuleDefinition.LOGICAL_NOT_OPERATOR:
					result = !((Boolean) currentOperand.evaluate(currentModel,
							false)).booleanValue();
					break;					

//					  ----------------------------------------------------------
//					  ------------------------   EQUALS   ------------------------
//					  ----------------------------------------------------------
				case RuleDefinition.EQUALS_OPERATOR:
					result = currentOperand.evaluate(currentModel,
							false).equals(
							((RuleInstance) operands.elementAt(i+1)).evaluate(
									currentModel, false));
					i = operands.size();
					break;					

//					  ----------------------------------------------------------
//					  ------------------------   >   ------------------------
//					  ----------------------------------------------------------
				case RuleDefinition.GREATER_THAN_OPERATOR:
					if (currentOperand.getDefinition().getValueType() ==
						RuleDefinition.INT_VALUE) {
						
						result = ((Integer) currentOperand.evaluate(currentModel,
								onlyCached)).compareTo(
										(Integer) ((RuleInstance) operands.elementAt(i+1)).
										evaluate(currentModel,
												onlyCached)) > 0;
					}
					else if (currentOperand.getDefinition().getValueType() ==
								RuleDefinition.LONG_VALUE || 
							currentOperand.getDefinition().getValueType() ==
								RuleDefinition.TIMESTAMP_VALUE) {
						result = ((Long) currentOperand.evaluate(
								currentModel,
								onlyCached)).compareTo(
										(Long) ((RuleInstance) operands.elementAt(i+1)).
										evaluate(currentModel,
												onlyCached)) > 0;
					}
					
					if (result == true) {
						currentDecision.setStatus(
								PolicyDecision.STATUS_FINAL);
					}
					else if (onlyCached) {
						onlyCached = false;
						
						if (currentOperand.getDefinition().getValueType() ==
							RuleDefinition.INT_VALUE) {
							result = ((Integer) currentOperand.evaluate(currentModel,
									onlyCached)).compareTo(
											(Integer) ((RuleInstance) operands.elementAt(i+1)).
											evaluate(currentModel,
													onlyCached)) > 0;
						}
						else if (currentOperand.getDefinition().getValueType() ==
							RuleDefinition.LONG_VALUE || 
							currentOperand.getDefinition().getValueType() ==
								RuleDefinition.TIMESTAMP_VALUE) {
							result = ((Long) currentOperand.evaluate(
									currentModel,
									onlyCached)).compareTo(
											(Long) ((RuleInstance) operands.elementAt(i+1)).
											evaluate(currentModel,
													onlyCached)) > 0;
						}
						
						if (result == false) {
							currentDecision.setStatus(
									PolicyDecision.STATUS_FINAL);
						}
					}

					i = operands.size();
					break;					

//					  ----------------------------------------------------------
//					  ------------------------   <   ------------------------
//					  ----------------------------------------------------------
				case RuleDefinition.LESSER_THAN_OPERATOR:
					if (currentOperand.getDefinition().getValueType() ==
								RuleDefinition.INT_VALUE) {
						result = ((Integer) currentOperand.evaluate(currentModel,
								onlyCached)).compareTo(
								(Integer) ((RuleInstance) operands.elementAt(i+1)).
								evaluate(currentModel,
										onlyCached)) < 0;
					}
					else if (currentOperand.getDefinition().getValueType() ==
								RuleDefinition.LONG_VALUE || 
								currentOperand.getDefinition().getValueType() ==
									RuleDefinition.TIMESTAMP_VALUE) {
						result = ((Long) currentOperand.evaluate(
								currentModel,
								onlyCached)).compareTo(
								(Long) ((RuleInstance) operands.elementAt(i+1)).
								evaluate(currentModel,
										onlyCached)) < 0;
					}
					
					if (result == false) {
						currentDecision.setStatus(
								PolicyDecision.STATUS_FINAL);
					}
					else if (onlyCached) {
						onlyCached = false;
						currentOperand.resetEvaluation(currentModel);
						
						if (currentOperand.getDefinition().getValueType() ==
							RuleDefinition.INT_VALUE) {
							result = ((Integer) currentOperand.evaluate(
									currentModel,
									onlyCached)).compareTo(
											(Integer) ((RuleInstance) operands.elementAt(i+1)).
											evaluate(currentModel,
													onlyCached)) < 0;
						}
						else if (currentOperand.getDefinition().getValueType() ==
							RuleDefinition.LONG_VALUE || 
							currentOperand.getDefinition().getValueType() ==
								RuleDefinition.TIMESTAMP_VALUE) {
							result = ((Long) currentOperand.evaluate(
									currentModel,
									onlyCached)).compareTo(
											(Long) ((RuleInstance) operands.elementAt(i+1)).
											evaluate(currentModel,
													onlyCached)) < 0;
						}
						
						if (result == false) {
							currentDecision.setStatus(
									PolicyDecision.STATUS_FINAL);
						}
					}
					
					i = operands.size();
					break;					
				default:
					break;
				}
			}
			
			finalResult = new Boolean(result);
		}

//		  ----------------------------------------------------------
//		  ------------------------   NUMERIC   ------------------------
//		  ----------------------------------------------------------
		else {
			// init
			boolean intVsLong = true;
			int intResult = 0;
			long longResult = 0;

			for (int i = 0; i < operands.size(); i++) {
				currentOperand = (RuleInstance) operands.elementAt(i);
				intVsLong = (currentOperand.getDefinition().getValueType() ==
					RuleDefinition.INT_VALUE);
				
				switch (definition.getType()) {

//				  ----------------------------------------------------------
//				  ------------------------   +   ------------------------
//				  ----------------------------------------------------------
				case RuleDefinition.ADD_OPERATOR:
					if (intVsLong) {
						intResult += ((Integer) currentOperand.evaluate(
								currentModel, onlyCached)).intValue();
					}
					else {
						longResult += ((Long) currentOperand.evaluate(
								currentModel, onlyCached)).longValue();
					}
					break;					

//					  ----------------------------------------------------------
//					  ------------------------   -   ------------------------
//					  ----------------------------------------------------------
				case RuleDefinition.SUBTRACT_OPERATOR:
					if (intVsLong) {
						if (i == 0) {
							intResult = ((Integer) currentOperand.evaluate(
									currentModel, onlyCached)).intValue();
						}
						else {
							intResult -= ((Integer) currentOperand.evaluate(
									currentModel, onlyCached)).intValue();
						}
					}
					else {
						if (i == 0) {
							longResult = ((Long) currentOperand.evaluate(
									currentModel, onlyCached)).longValue();
						}
						else {
							longResult -= ((Long) currentOperand.evaluate(
									currentModel, onlyCached)).longValue();
						}
					}
					break;					

//					  ----------------------------------------------------------
//					  ------------------------   *   ------------------------
//					  ----------------------------------------------------------
				case RuleDefinition.MULTIPLY_OPERATOR:
					if (intVsLong) {
						if (i == 0) {
							intResult = ((Integer) currentOperand.evaluate(
									currentModel, onlyCached)).intValue();
						}
						else {
							intResult *= ((Integer) currentOperand.evaluate(
									currentModel, onlyCached)).intValue();
						}
					}
					else {
						if (i == 0) {
							longResult = ((Long) currentOperand.evaluate(
									currentModel, onlyCached)).longValue();
						}
						else {
							longResult *= ((Long) currentOperand.evaluate(
									currentModel, onlyCached)).longValue();
						}
					}
					break;					

//					  ----------------------------------------------------------
//					  ------------------------   /   ------------------------
//					  ----------------------------------------------------------
				case RuleDefinition.DIVIDE_OPERATOR:
					if (intVsLong) {
						if (i == 0) {
							intResult = ((Integer) currentOperand.evaluate(
									currentModel, onlyCached)).intValue();
						}
						else {
							intResult /= ((Integer) currentOperand.evaluate(
									currentModel, onlyCached)).intValue();
						}
					}
					else {
						if (i == 0) {
							longResult = ((Long) currentOperand.evaluate(
									currentModel, onlyCached)).longValue();
						}
						else {
							longResult /= ((Long) currentOperand.evaluate(
									currentModel, onlyCached)).longValue();
						}
					}
					break;					
				default:
					break;
				}
			}

			if (intVsLong) {
				finalResult = new Integer(intResult);
			}
			else {
				finalResult = new Long(longResult);
			}
		}
		
		return (finalResult);
	} // evaluate


	public void setOperands(Vector operands) {
		this.operands = operands;
	}

	
	public Vector getOperands() {
		return operands;
	}
	
	
}; // CompositeExpression
